package main

import (
	"errors"
	"fmt"
	"github.com/dgrijalva/jwt-go"
	"github.com/gofiber/fiber/v2"
	"github.com/gofiber/fiber/v2/middleware/cors"
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"
	"log"
	"strings"
	"time"
)

type ProductType struct {
	Id   int `gorm:"primary_key:id"`
	Name string
	//CreatedAt time.Time
	//UpdatedAt time.Time
}

type Product struct {
	Id     int `gorm:"primary_key:id"`
	Name   string
	Type   ProductType `gorm:"ForeignKey:TypeId;AssociationForeignKey:Id"`
	TypeId int
	//CreatedAt time.Time
	//UpdatedAt time.Time
}

type ProjectType struct {
	Id   int `gorm:"primary_key:id"`
	Name string
	//CreatedAt time.Time
	//UpdatedAt time.Time
}

type Project struct {
	Id     int `gorm:"primary_key:id"`
	Name   string
	Type   ProjectType `gorm:"ForeignKey:TypeId;AssociationForeignKey:Id"`
	TypeId int
	//CreatedAt time.Time
	//UpdatedAt time.Time
}

type Meal struct {
	Id          int `gorm:"primary_key:id"`
	Name        string
	ProjectList []*Project `gorm:"many2many:meal_projects;"`
	ProductList []*Product `gorm:"many2many:meal_products;"`
	//CreatedAt time.Time
	//UpdatedAt time.Time
}

type Member struct {
	Id       int `gorm:"primary_key:id"`
	Name     string
	Meallist []*Meal `gorm:"many2many:member_meals;"`
	//CreatedAt time.Time
	//UpdatedAt time.Time
}

type ResponseData struct {
	Code    int
	Data    []*Member
	Message string
}

type MemberClaims struct {
	UserId   int    `json:"userid"`
	Username string `json:"username"`
	Password string `json:"password"`
	RoleId   int    `json:"roleid"`
	jwt.StandardClaims
}

type UserInfo struct {
	UserId   int
	Username string `json:"username"`
	Password string `json:"password"`
	RoleId   int
}

var (
	db  *gorm.DB
	err error
)

const TokenExpireDuration = time.Hour * 2

var MemberSecret = []byte("你值得拥有")

// GenToken 生成JWT
func GenToken(user *UserInfo) (string, error) {
	// priKeyBytes, err := ioutil.ReadFile("./pem/private.pem")
	// if err != nil {
	// 	fmt.Println("私钥文件读取失败")
	// }

	// priKey, err := jwt.ParseRSAPrivateKeyFromPEM(priKeyBytes)
	// if err != nil {
	// 	fmt.Println("私钥文件不正确")
	// }

	// 创建一个我们自己的声明
	c := MemberClaims{
		user.UserId, // 自定义字段
		user.Username,
		user.Password,
		user.RoleId,
		jwt.StandardClaims{
			ExpiresAt: time.Now().Add(TokenExpireDuration).Unix(), // 过期时间
			Issuer:    "member-project",                           // 签发人
		},
	}
	// 使用指定的签名方法创建签名对象
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, c)
	// 使用指定的secret签名并获得完整的编码后的字符串token
	return token.SignedString(MemberSecret)

	//token := jwt.NewWithClaims(jwt.SigningMethodRS256, c)
	//return token.SignedString(priKey)
}

// ParseToken 解析JWT
func ParseToken(tokenString string) (*MemberClaims, error) {

	// pubKeyBytes, err := ioutil.ReadFile("./pem/public.pem")
	// if err != nil {
	// 	fmt.Println("公钥文件读取失败")
	// }

	// pubKey, err := jwt.ParseRSAPublicKeyFromPEM()(pubKeyBytes)
	// if err != nil {
	// 	fmt.Println("公钥文件不正确")
	// }

	// 解析token
	// token, err := jwt.ParseWithClaims(tokenString, &MemberClaims{}, func(token *jwt.Token) (i interface{}, err error) {
	// 	return pubKey, nil
	// })

	token, err := jwt.ParseWithClaims(tokenString, &MemberClaims{}, func(token *jwt.Token) (i interface{}, err error) {
		return MemberSecret, nil
	})
	if err != nil {
		return nil, err
	}
	if claims, ok := token.Claims.(*MemberClaims); ok && token.Valid { // 校验token
		return claims, nil
	} else if ve, ok := err.(jwt.ValidationError); ok {
		if ve.Errors&jwt.ValidationErrorMalformed != 0 {
			fmt.Println("错误的token")
		} else if ve.Errors&(jwt.ValidationErrorExpired|jwt.ValidationErrorNotValidYet) != 0 {
			fmt.Println(" token过期或未启用")
		} else {
			fmt.Println("Could not handle this token:", err)
		}
	} else {
		fmt.Println("无法解析此token", err)
	}

	return nil, errors.New("invalid token")
}

func authHandler(c *fiber.Ctx) error {
	// 用户发送用户名和密码过来
	var user UserInfo
	err := c.BodyParser(&user)
	fmt.Println(user)

	if err != nil {
		if err := c.JSON(fiber.Map{
			"code":    2001,
			"message": "无效的参数",
		}); err != nil {
			c.Status(500).Send(([]byte)(err.Error()))
			return err
		}
		return nil
	}

	// 校验用户名和密码是否正确
	var ui UserInfo
	db.Where("username = ?", user.Username).Where("password = ?", user.Password).First(&ui)
	if ui.UserId != 0 {
		// 生成Token
		tokenString, _ := GenToken(&ui)

		if err := c.JSON(fiber.Map{
			"code":    2000,
			"message": "success",
			"data":    fiber.Map{"token": tokenString},
		}); err != nil {
			c.Status(500).Send(([]byte)(err.Error()))
			return err
		}
		return nil
	}

	if err := c.JSON(fiber.Map{
		"code":    2002,
		"message": "鉴权失败",
	}); err != nil {
		c.Status(500).Send(([]byte)(err.Error()))
		return err
	}
	return nil
}

// JWTAuthMiddleware 基于JWT的认证中间件
func JWTAuthMiddleware() func(c *fiber.Ctx) error {
	return func(c *fiber.Ctx) error {
		// 客户端携带Token有三种方式 1.放在请求头 2.放在请求体 3.放在URI
		// 这里假设Token放在Header的Authorization中，并使用Bearer开头
		// 这里的具体实现方式要依据你的实际业务情况决定

		//url传递token
		//authHeader := c.Query("token")
		//if authHeader == "" {
		//	c.JSON(http.StatusOK, gin.H{
		//		"code": 2003,
		//		"msg":  "请求头中auth为空",
		//	})
		//	c.Abort()
		//	return
		//}

		//from传递token
		// authHeader := c.PostForm("token")
		// if authHeader == "" {
		// 	c.JSON(http.StatusOK, gin.H{
		// 		"code": 2003,
		// 		"msg":  "请求头中auth为空",
		// 	})
		// 	c.Abort()
		// 	return
		// }

		//请求头传递token
		authHeader := c.Get("token")
		if authHeader == "" {
			if err := c.JSON(fiber.Map{
				"code":    2003,
				"message": "请求头中auth为空",
			}); err != nil {
				c.Status(500).Send(([]byte)(err.Error()))
				return err
			}
			return nil
		}

		// 按空格分割
		parts := strings.SplitN(authHeader, " ", 2)
		if !(len(parts) == 2 && parts[0] == "SuperToken") {
			if err := c.JSON(fiber.Map{
				"code":    2004,
				"message": "请求头中auth格式有误",
			}); err != nil {
				c.Status(500).Send(([]byte)(err.Error()))
				return err
			}
			return nil
		}

		// parts[1]是获取到的tokenString，我们使用之前定义好的解析JWT的函数来解析它
		mc, err := ParseToken(parts[1])
		if err != nil {
			if err := c.JSON(fiber.Map{
				"code":    2005,
				"message": err.Error(),
			}); err != nil {
				c.Status(500).Send(([]byte)(err.Error()))
				return err
			}
			return nil
		}

		// 将当前请求的username信息保存到请求的上下文c上
		fmt.Println(*mc)
		c.Set("username", mc.Username)
		c.Next() // 后续的处理函数可以用过c.Get("username")来获取当前请求的用户信息

		return nil
	}
}

func main() {

	db, err = gorm.Open("mysql", "root:new_password@tcp(localhost:3306)/gorm_insert")
	if err != nil {
		panic("failed to connect database")
	}
	defer db.Close()

	db.AutoMigrate(&ProductType{}, &Product{}, &ProjectType{}, &Project{}, &Meal{}, &Member{}, &UserInfo{})

	app := fiber.New()
	app.Use(cors.New(cors.Config{
		AllowOrigins:     "*",
		AllowCredentials: true,
		MaxAge:           3600,
		ExposeHeaders:    "*",
		AllowHeaders:     "*",
	}))

	app.Post("/login", authHandler)

	app.Post("/insertMember", JWTAuthMiddleware(), func(c *fiber.Ctx) error {

		//username := c.FormValue("username")
		//address := c.FormValue("address")

		var mb []*Member

		if err := c.BodyParser(&mb); err != nil {
			log.Fatal(err)
		}

		//if errj := json.Unmarshal([]byte(address), &mb); errj == nil{

		for _, jv := range mb {
			var m Member
			db.Where("id= ?", jv.Id).First(&m)
			if m.Id == 0 {
				db.Create(jv)
			}
			for _, mv := range jv.Meallist {
				var ml Meal
				db.Where("id= ?", mv.Id).First(&ml)
				if ml.Id == 0 {
					db.Create(mv)
				}
				db.Model(jv).Association("Meallist").Append(mv)

				for _, mvj := range mv.ProjectList {
					var jt ProjectType
					db.Where("id= ?", mvj.TypeId).First(&jt)
					if jt.Id == 0 {
						db.Create(mvj.Type)
					}

					var j Project
					db.Where("id= ?", mvj.Id).First(&j)
					if j.Id == 0 {
						db.Create(mvj)
					}
					db.Model(mv).Association("ProjectList").Append(mvj)

				}
				for _, mvd := range mv.ProductList {
					var dt ProductType
					db.Where("id= ?", mvd.TypeId).First(&dt)
					if dt.Id == 0 {
						db.Create(mvd.Type)
					}
					var d Product
					db.Where("id= ?", mvd.Id).First(&d)
					if d.Id == 0 {
						db.Create(mvd)
					}
					db.Model(mv).Association("ProductList").Append(mvd)
				}
			}
		}

		if err := c.JSON(fiber.Map{
			"code":    200,
			"message": "插入成功",
			"data":    nil,
		}); err != nil {
			c.Status(500).Send(([]byte)(err.Error()))
			return err
		}

		//} else {
		//	c.Status(500).Send(err)
		//	return
		//}

		return nil
	})

	app.Post("/queryMember", JWTAuthMiddleware(), func(c *fiber.Ctx) error {

		var m []*Member
		db.Find(&m)
		for _, v := range m {
			db.Joins("JOIN member_meals ON member_meals.meal_id = meals.id AND member_meals.member_id = ?", v.Id).Find(&v.Meallist)

			for _, mv := range v.Meallist {
				db.Joins("JOIN meal_projects ON meal_projects.project_id = projects.id AND meal_projects.meal_id = ?", mv.Id).Find(&mv.ProjectList)

				for _, jv := range mv.ProjectList {
					db.Joins("JOIN projects ON project_types.id = projects.type_id AND projects.id = ?", jv.Id).Find(&jv.Type)
				}

				db.Joins("JOIN meal_products ON meal_products.product_id = products.id AND meal_products.meal_id = ?", mv.Id).Find(&mv.ProductList)

				for _, dv := range mv.ProductList {
					db.Joins("JOIN products ON product_types.id = products.type_id AND products.id = ?", dv.Id).Find(&dv.Type)

				}
			}
		}

		data := ResponseData{
			Code:    200,
			Data:    m,
			Message: "成功",
		}

		if err := c.JSON(data); err != nil {
			c.Status(500).Send(([]byte)(err.Error()))
			return err
		}

		return nil
	})

	app.Get("/file", func(c *fiber.Ctx) error {
		fmt.Println(c.Query("id"))
		return c.SendFile("E:/Chrome/WeChat Files/wxid_piiuwaaajk7c22/FileStorage/File/2022-03/TestReport202203030410.html")
	})

	//app.Static("/file", "E:\\Chrome\\WeChat Files\\wxid_piiuwaaajk7c22\\FileStorage\\File\\2022-03\\TestReport202203030410.html")

	app.Get("/download", func(c *fiber.Ctx) error {
		
		err := c.Download("F:/hot_change/android/", "98e536200ff4465a110a86103a02249f.apk")
		fmt.Println("err：", err)

		return nil
	})

	app.Listen(":5390")
}
